package com.xmmj.baselibrary.utils.websocket

import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import org.java_websocket.enums.ReadyState
import org.java_websocket.handshake.ServerHandshake
import java.net.URI
import java.util.concurrent.atomic.AtomicBoolean

/**
 * WebSocket工具类
 */
class WebSocketManager private constructor(builder: Builder) : IWebSocket {
    /**
     * 心跳周期
     */
    private val mHeartbeatPeriod: Long

    /**
     * 重连尝试周期
     */
    private val mReconnectPeriod: Long

    /**
     * 心跳
     */
    private var mHeartBeatRunnable: Runnable? = null

    /**
     * 断线重连
     */
    private var mReconnectRunnable: Runnable? = null

    /**
     * 服务器地址
     */
    private val mAddress: String?

    /**
     * 心跳包数据
     */
    private var mHeartbeatData: String?

    /**
     * 是否调试模式
     */
    private var isDebug: Boolean

    /**
     * 客户端是否验证心跳包
     */
    private val isVerifyHeartBeat: Boolean

    /**
     * WebSocket事件监听器
     */
    private val mWebSocketEventListener: OnWebSocketEventListener?
    private val mHandler: MyHandler?
    private var mClient: JWebSocketClient? = null

    /**
     * 是否主动断开，主动断开不启动重连任务
     */
    private val isInitiativeDisconnect = AtomicBoolean(false)

    /**
     * 是否收到服务端心跳返回
     */
    private val isReceiverHeartbeatData = AtomicBoolean(true)

    /**
     * 是否已销毁
     */
    private val isDeStory = AtomicBoolean(false)

    /**
     * 最新一次状态记录
     */
    private val mLastStatus = ReadyState.NOT_YET_CONNECTED

    /**
     * 初始化webSocket连接
     */
    @Synchronized
    private fun initSocketClient() {
        if (isDeStory.get()) {
            log("WebSocketManager is destroy")
            return
        }
        log("initSocketClient")
        log("address=$mAddress")
        var uri: URI? = null
        try {
            uri = URI.create(mAddress)
        } catch (e: Exception) {
            e.printStackTrace()
            log(e.message)
        }
        if (uri == null) {
            return
        }
        mClient = object : JWebSocketClient(uri, isDebug) {
            override fun onMessage(message: String) {
                mHandler!!.post(object : MyRunnable() {
                    override fun run() {
                        if (null != mWebSocketEventListener && isStateChange) {
                            mWebSocketEventListener.onReceiverMsg(message)
                        }
                    }
                })
            }

            override fun onClose(code: Int, reason: String, remote: Boolean) {
                super.onClose(code, reason, remote)
                mHandler!!.post(object : MyRunnable() {
                    override fun run() {
                        if (null != mWebSocketEventListener && isStateChange) {
                            mWebSocketEventListener.onDisconnected(code, reason, remote)
                        }
                    }
                })
                //停止心跳发送
                stopHeatBeatTask()
                if (!isInitiativeDisconnect.get()) {
                    //非主动断开连接，启动重连任务
                    startReconnectTask()
                }
            }

            override fun onOpen(serverHandshake: ServerHandshake) {
                super.onOpen(serverHandshake)
                mHandler!!.post(object : MyRunnable() {
                    override fun run() {
                        if (null != mWebSocketEventListener && isStateChange) {
                            mWebSocketEventListener.onConnected(serverHandshake)
                        }
                    }
                })
                //启动心跳发送
                startHeatBeatTask()
                //关闭重连任务
                stopReconnectTask()
            }
        }
    }

    private inner class ConnectThread : Thread() {
        override fun run() {
            synchronized(WebSocketManager::class.java) {
                log("ConnectThread start")
                if (null == mClient) {
                    initSocketClient()
                }
                if (isConnected()) {
                    return
                }
                if (mClient == null) {
                    log("mClient is null")
                    return
                }
                if (ReadyState.NOT_YET_CONNECTED == mClient!!.readyState) {
                    log("webSocket connectBlocking")
                    try {
                        mClient!!.connectBlocking()
                        //如果block期间已执行销毁 则断开连接
                        if (isDeStory.get()) {
                            disconnect()
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                } else {
                    log("webSocket reconnectBlocking")
                    try {
                        mClient!!.reconnectBlocking()
                        //如果block期间已执行销毁 则断开连接
                        if (isDeStory.get()) {
                            disconnect()
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                }
            }
        }
    }

    private inner class CloseThread : Thread() {
        override fun run() {
            synchronized(WebSocketManager::class.java) {
                log("CloseThread start")
                if (!isConnected()) {
                    return
                }
                log("webSocket closeBlocking")
                try {
                    mClient!!.closeBlocking()
                    //如果block期间已执行销毁 将mClient置空
                    if (isDeStory.get()) {
                        mClient = null
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }
    }

    /**
     * 判断状态是否改变
     */
    private val isStateChange: Boolean
        get() = mClient != null && mClient!!.readyState != mLastStatus

    /**
     * 发起连接
     */
    override fun connect() {
        if (isConnected()) {
            return
        }
        isInitiativeDisconnect.set(false)
        ConnectThread().start()
    }

    /**
     * 断开连接
     */
    override fun disconnect() {
        isInitiativeDisconnect.set(true)
        removeHandlerTask()
        if (isConnected()) {
            CloseThread().start()
        }
    }

    /**
     * 主动断开之后再次重连，用于异常状态尝试重连
     */
    override fun disconnectAndReconnect() {
        isInitiativeDisconnect.set(false)
        removeHandlerTask()
        if (isConnected()) {
            //已连接则断开 之后会走重连逻辑
            CloseThread().start()
        } else {
            //未连接则连接
            connect()
        }
    }

    /**
     * 销毁
     */
    override fun destory() {
        isDeStory.set(true)
        disconnect()
    }

    /**
     * 更新心跳包接收状态
     */
    override fun refreshHeartFlag() {
        isReceiverHeartbeatData.set(true)
    }

    /**
     * 设置心跳包数据
     *
     * @param heartBeatData 心态数据
     */
    override fun setHeartBeatData(heartBeatData: String?) {
        mHeartbeatData = heartBeatData
    }

    /**
     * 判断是否连接状态
     */
    override fun isConnected(): Boolean {
        return null != mClient && mClient!!.isOpen
    }

    override fun setDebug(debug: Boolean) {
        isDebug = debug
        if (mClient != null) {
            mClient!!.setDebug(debug)
        }
    }

    /**
     * 启动心跳发送
     */
    private fun startHeatBeatTask() {
        if (TextUtils.isEmpty(mHeartbeatData)) {
            return
        }
        //首个心跳包没有上一个心跳包，所以默认已接收服务端返回
        isReceiverHeartbeatData.set(true)
        if (null == mHeartBeatRunnable) {
            mHeartBeatRunnable = object : MyRunnable() {
                override fun run() {
                    sendHeartBeat()
                    mHandler!!.postDelayed(this, mHeartbeatPeriod)
                }
            }
        } else {
            mHandler!!.removeCallbacks(mHeartBeatRunnable!!)
        }
        mHandler!!.post(mHeartBeatRunnable!!)
    }

    /**
     * 停止心跳发送
     */
    private fun stopHeatBeatTask() {
        if (null != mHeartBeatRunnable) {
            mHandler!!.removeCallbacks(mHeartBeatRunnable!!)
        }
    }

    /**
     * 启动重连任务
     */
    private fun startReconnectTask() {
        if (null == mReconnectRunnable) {
            mReconnectRunnable = object : MyRunnable() {
                override fun run() {
                    log("webSocket reconnecting")
                    connect()
                }
            }
        } else {
            mHandler!!.removeCallbacks(mReconnectRunnable!!)
        }
        mHandler!!.postDelayed(mReconnectRunnable!!, mReconnectPeriod)
    }

    /**
     * 停止重连任务
     */
    private fun stopReconnectTask() {
        if (null != mReconnectRunnable) {
            mHandler!!.removeCallbacks(mReconnectRunnable!!)
        }
    }

    /**
     * 发送消息
     *
     * @param msg 消息
     */
    override fun sendMsg(msg: String?) {
        if (isConnected() && !TextUtils.isEmpty(msg)) {
            log("send：$msg")
            try {
                mClient!!.send(msg)
            } catch (e: Exception) {
                e.printStackTrace()
                log(e.message)
            }
        }
    }

    /**
     * 发送心跳包
     */
    private fun sendHeartBeat() {
        if (null == mClient || !mClient!!.isOpen) {
            log("mClient is null or not open")
            return
        }
        if (isVerifyHeartBeat && !isReceiverHeartbeatData.get()) {
            //isVerifyHeartBeat==true才验证心跳包
            //如果上次发送的心跳包服务端没返回则认为心跳中断，尝试重连
            log("HeartBeat UnReceiver Reconnecting")
            disconnectAndReconnect()
            return
        }
        isReceiverHeartbeatData.set(false)
        sendMsg(mHeartbeatData)
    }

    /**
     * 移除handler未执行的任务
     */
    private fun removeHandlerTask() {
        if (null == mHandler) {
            return
        }
        if (null != mHeartBeatRunnable) {
            mHandler.removeCallbacks(mHeartBeatRunnable!!)
        }
        if (null != mReconnectRunnable) {
            mHandler.removeCallbacks(mReconnectRunnable!!)
        }
    }

    internal class MyHandler(looper: Looper?) : Handler(looper!!)
    internal abstract class MyRunnable : Runnable

    private fun log(msg: String?) {
        if (isDebug) {
            WSLog.log(msg)
        }
    }

    /**
     * 构造者模式
     */
    class Builder {
        /**
         * 心跳周期,默认20秒
         */
        var heartbeatPeriod: Long = 20000

        /**
         * 重连尝试周期，默认3秒
         */
        var reconnectPeriod: Long = 3000

        /**
         * 服务器地址
         */
        var address: String? = null

        /**
         * 心跳包内容
         */
        var heartbeatData: String? = null

        /**
         * 客户端是否验证心跳包
         */
        var verifyHeartBeat = false

        /**
         * 事件回调
         */
        var webSocketEventListener: OnWebSocketEventListener? = null

        /**
         * 是否调试模式
         */
        var debug = true

        /**
         * 客户端是否验证心跳包，验证的话需要在[OnWebSocketEventListener.onReceiverMsg]接收消息时判断是心跳包，然后调用[IWebSocket.refreshHeartFlag]
         * 刷新心跳标识，表示已收到服务器返回心跳包，否则判定为未收到心跳包，框架会主动断开连接尝试重连。
         * 心跳标识在这次发送心跳设置成false 下次发送心跳前验证上次发送的的标识是否置成true
         *
         * @param verifyHeartBeat 是否验证
         */
        fun setVerifyHeartBeat(verifyHeartBeat: Boolean): Builder {
            this.verifyHeartBeat = verifyHeartBeat
            return this
        }

        /**
         * 设置调试(日志)模式
         *
         * @param debug 是否调试
         */
        fun setDebug(debug: Boolean): Builder {
            this.debug = debug
            return this
        }

        /**
         * 设置心跳周期
         *
         * @param heartbeatPeriod 毫秒
         */
        fun setHeartbeatPeriod(heartbeatPeriod: Long): Builder {
            this.heartbeatPeriod = heartbeatPeriod
            return this
        }

        /**
         * 设置重连尝试周期
         *
         * @param reconnectPeriod 毫秒
         */
        fun setReconnectPeriod(reconnectPeriod: Long): Builder {
            this.reconnectPeriod = reconnectPeriod
            return this
        }

        /**
         * 设置地址
         *
         * @param address 服务器地址
         */
        fun setWebSocketAddress(address: String?): Builder {
            this.address = address
            return this
        }

        /**
         * 设置心跳包内容
         *
         * @param heartbeatData 心跳数据
         */
        fun setHeartbeatData(heartbeatData: String?): Builder {
            this.heartbeatData = heartbeatData
            return this
        }

        fun setWebSocketEventListener(webSocketEventListener: OnWebSocketEventListener?): Builder {
            this.webSocketEventListener = webSocketEventListener
            return this
        }

        fun build(): WebSocketManager {
            require(!TextUtils.isEmpty(address)) { "address is null" }
            require(heartbeatPeriod >= 1000) { "heartbeat period must be greater than 1s" }
            require(reconnectPeriod >= 1000) { "reconnect period must be greater than 1s" }
            return WebSocketManager(this)
        }

        companion object {
            fun create(): Builder {
                return Builder()
            }
        }
    }

    init {
        mHandler = MyHandler(Looper.getMainLooper())
        mHeartbeatPeriod = builder.heartbeatPeriod
        mReconnectPeriod = builder.reconnectPeriod
        mAddress = builder.address
        mHeartbeatData = builder.heartbeatData
        mWebSocketEventListener = builder.webSocketEventListener
        isDebug = builder.debug
        isVerifyHeartBeat = builder.verifyHeartBeat
    }
}